6-4 优化镜像体积压缩,镜像打包优化
优化目标
将镜像从 526MB 进一步压缩到 400MB 左右。核心思路是使用之前封装的包含 pnpm 的自定义 Alpine 基础镜像作为构建阶段基础,最终运行阶段使用 node:alpine 镜像。
使用自定义基础镜像
之前通过 Dockerfile-pnpm 创建的基础镜像已经内置了 pnpm 及其配置。将其作为构建阶段的基础镜像:
# 使用自封装的 pnpm 基础镜像
FROM your-registry/node:18-alpine-pnpm AS base
dockerfile
这样基础镜像中已经配置好 pnpm,无需再设置 PNPM_HOME 等环境变量。
配置构建参数
# 支持 docker build --build-arg 动态指定 registry
ARG REGISTRY=https://registry.npmmirror.com
# 设置环境变量供后续阶段使用
ENV PNPM_REGISTRY=${REGISTRY}
ENV PNPM_STORE=/home/node/.local/share/pnpm/store
dockerfile
ARG REGISTRY 的作用是当淘宝源连接异常时,可以通过构建参数指定其他源:
docker build --build-arg REGISTRY=https://registry.npmjs.org -t nest-starter:1.4 .
bash
注意将 PNPM_STORE 路径与基础镜像中的存储路径保持一致,以便复用缓存。
Prod Deps 阶段
FROM base AS prod-deps
USER node
WORKDIR /home/node/app
# 设置工作目录权限
RUN chown -R node:node /home/node/app
# 复制依赖声明文件(带权限)
COPY --chown=node:node package*.json pnpm-lock.yaml ./
# 设置镜像源
RUN pnpm config set registry ${PNPM_REGISTRY}
# 安装生产依赖
RUN pnpm install --prod --frozen-lockfile
dockerfile
所有 COPY 和 RUN 命令都需要带上 --chown=node:node,确保文件归属正确。
Build 阶段
FROM base AS build
USER node
WORKDIR /home/node/app
RUN chown -R node:node /home/node/app
# 复制所有源码(带权限)
COPY --chown=node:node . .
# 设置镜像源
RUN pnpm config set registry ${PNPM_REGISTRY}
# 安装全部依赖
RUN pnpm install --frozen-lockfile
# 生成 Prisma 客户端
RUN pnpm run build:clients
# 编译项目
RUN pnpm run build:prod
dockerfile
Final 阶段:使用 Alpine 镜像
FROM node:18-alpine AS final
# 最先指定用户
USER node
WORKDIR /home/node/app
# 从 prod-deps 阶段拷贝 node_modules(排除缓存)
COPY --from=prod-deps --chown=node:node /home/node/app/node_modules ./node_modules
# 从 build 阶段拷贝 Prisma 客户端
COPY --from=build --chown=node:node /home/node/app/prisma ./prisma
# 从 build 阶段拷贝编译产物
COPY --from=build --chown=node:node /home/node/app/dist ./dist
CMD ["node", "dist/main.js"]
dockerfile
最终运行阶段使用纯 node:18-alpine 镜像,不包含 pnpm 及其缓存,体积最小。
补充 .dockerignore
由于 Prisma 客户端是在构建过程中动态生成的,可以将开发环境中已生成的客户端目录加入 .dockerignore,避免不必要的文件拷贝:
prisma/clients
.vscode
.git
node_modules
dist
text
系统级和 IDE 相关的目录也应该加入忽略列表。
优化效果
# 构建镜像
docker build -t nest-starter:1.4 .
# 查看最终镜像大小
docker images | grep nest
# nest-starter 1.4 约 400MB(从最初的 800MB 减少到一半)
# 查看 Alpine 基础镜像大小
docker images | grep node
# node 18-alpine 约 126MB
bash
体积分析:
| 组成部分 | 大小 |
|---|---|
| Node Alpine 基础镜像 | ~126MB |
| 应用代码 + Prisma 客户端 + 依赖 | ~280MB |
| 合计 | ~406MB |
相比最初的 800MB,压缩了约 50%。
优化路径总结
v1.0 (800MB) -> v1.1 (800MB) -> v1.3 (526MB) -> v1.4 (400MB)
原始 基础镜像 slim+corepack Alpine+自定义基础镜像
text
| 版本 | 优化措施 | 镜像大小 |
|---|---|---|
| v1.0 | 初始多阶段构建 | ~800MB |
| v1.3 | 使用 corepack + 四阶段构建 | ~526MB |
| v1.4 | 自定义基础镜像 + Alpine 运行时 | ~400MB |
小结
- 使用自定义 pnpm 基础镜像作为构建阶段基础,运行阶段使用纯 Alpine 镜像
- 通过
ARG支持动态切换 registry 源,提升构建灵活性 - 所有文件操作加
--chown=node:node确保权限正确 - 从
prod-deps阶段拷贝node_modules,排除 pnpm 缓存 - 合理配置
.dockerignore,避免不必要的文件进入构建上下文 - 最终镜像约 400MB,应用本身仅占约 280MB
↑